Skip to content

feat(everything): add tool annotations to all tools#3891

Open
m1lestones wants to merge 2 commits intomodelcontextprotocol:mainfrom
m1lestones:feat/tool-annotations-everything-server
Open

feat(everything): add tool annotations to all tools#3891
m1lestones wants to merge 2 commits intomodelcontextprotocol:mainfrom
m1lestones:feat/tool-annotations-everything-server

Conversation

@m1lestones
Copy link
Copy Markdown

Summary

Adds ToolAnnotations (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) to all 17 tools in the everything server, consistent with the pattern already established in the git (#3545), time (#3581), and sequential-thinking (#3534) servers.

The everything server is designed to showcase all MCP protocol capabilities — ToolAnnotations are a core protocol feature and their absence here was an oversight.

Annotation rationale

Tool readOnly destructive idempotent openWorld
echo
get-sum
get-env
get-tiny-image
get-annotated-message
get-resource-links
get-resource-reference
get-roots-list
get-structured-content
gzip-file-as-resource
toggle-simulated-logging
toggle-subscriber-updates
trigger-long-running-operation
trigger-sampling-request
trigger-elicitation-request
trigger-sampling-request-async
trigger-elicitation-request-async

Notes:

  • get-roots-list uses idempotentHint: false because the roots list can change between calls as the client updates its workspace
  • gzip-file-as-resource and the sampling tools use openWorldHint: true because they fetch from external URLs or invoke LLM inference
  • simulate-research-query uses registerToolTask (experimental tasks API) which has a different config shape — not modified here

Test plan

  • npx tsc --noEmit — no type errors
  • npx vitest run — all 95 tests pass unchanged

Add ToolAnnotations (readOnlyHint, destructiveHint, idempotentHint,
openWorldHint) to all 17 tools in the everything server, consistent
with the pattern established in the git, time, and sequential-thinking
servers.

Annotation rationale:
- Read-only tools (echo, get-sum, get-env, get-tiny-image,
  get-annotated-message, get-resource-links, get-resource-reference,
  get-structured-content, trigger-long-running-operation):
  readOnlyHint=true, idempotentHint=true, openWorldHint=false
- get-roots-list: readOnlyHint=true, idempotentHint=false (roots can
  change between calls)
- gzip-file-as-resource: openWorldHint=true (fetches from network),
  readOnlyHint=false (registers a session resource)
- toggle-simulated-logging, toggle-subscriber-updates:
  readOnlyHint=false, idempotentHint=false (stateful toggles)
- trigger-sampling-request, trigger-sampling-request-async:
  openWorldHint=true (invokes LLM inference via client)
- trigger-elicitation-request, trigger-elicitation-request-async:
  readOnlyHint=false, idempotentHint=false (user interaction)

All 95 existing tests pass unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@cliffhall cliffhall added enhancement New feature or request server-everything Reference implementation for the Everything MCP server - src/everything labels Apr 20, 2026
@cliffhall
Copy link
Copy Markdown
Member

@claude review

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 21, 2026

Claude finished @cliffhall's task in 2m 28s —— View job


PR Review: feat(everything): add tool annotations to all tools

  • Gather context and understand the PR
  • Read changed files to examine implementation
  • Review annotation correctness and rationale
  • Check consistency with other servers
  • Post review findings

Overall: ✅ Looks good — well-reasoned, complete coverage, consistent with the pattern in other servers.

This is a clean, focused PR. All 17 registered tools receive explicit annotations, the rationale table in the PR description is sound, and it correctly excludes simulate-research-query (which uses registerToolTask with a different experimental config shape). A few minor observations below.


Minor observations

1. trigger-long-running-operation: readOnlyHint: true + idempotentHint: false is semantically in tension

src/everything/tools/trigger-long-running-operation.ts:20-25

annotations: {
  readOnlyHint: true,
  destructiveHint: false,
  idempotentHint: false,   // ← in tension with readOnlyHint: true
  openWorldHint: false,
},

Per the MCP spec, a readOnlyHint: true tool does not modify its environment — which generally implies idempotency (calling it N times with the same args leaves the environment in the same state as calling it once). Setting idempotentHint: false alongside readOnlyHint: true may confuse clients that infer idempotency from the read-only flag.

The most internally consistent choice would be either:

  • readOnlyHint: true, idempotentHint: true — this is strictly accurate since the tool produces identical output and makes no persistent state changes, or
  • readOnlyHint: false, idempotentHint: false — if you consider time-consuming notifications as a non-trivial side effect.

The PR's current choice is defensible ("each invocation runs its own independent operation"), but the combination is worth documenting or reconsidering for clarity in a reference server.


2. gzip-file-as-resource: openWorldHint: true is correct, but worth noting the conditional nature

src/everything/tools/gzip-file-as-resource.ts:51-56

The tool accepts both external HTTP/HTTPS URLs and data: URIs. When a data: URI is passed it has no open-world interaction; openWorldHint: true is the safe and correct choice here (the default input is an external GitHub URL), but a comment in the description or code noting this could help readers understand why a "file compressor" is flagged open-world.


3. All tools with readOnlyHint: true also explicitly set destructiveHint: false — good practice for a reference server

This is intentional and appropriate. Even though readOnlyHint: true implies non-destructiveness, being explicit is the right call for a showcase server. No issue here.


4. simulate-research-query exclusion is correct

registerToolTask (experimental tasks API) takes a different config object — there's no annotations key at the top level in that shape. The PR is right to leave it out. If/when that API gains annotation support, a follow-up PR can address it.


Summary

The annotation choices are accurate and well-considered. The only substantive point worth discussing before merge is #1 (trigger-long-running-operation's readOnlyHint: true + idempotentHint: false). Everything else is either correct as-is or a stylistic note. No functional regressions — TypeScript passes and all 95 tests remain green.

The tool sends progress notifications as side effects, so readOnlyHint
should be false. readOnly + non-idempotent was semantically inconsistent
per the MCP spec.
@m1lestones
Copy link
Copy Markdown
Author

Good catch on trigger-long-running-operation — updated in 6369f24.

Changed readOnlyHint: truereadOnlyHint: false. The tool sends progress notifications as side effects on every invocation, which means it's not truly read-only. readOnlyHint: false + idempotentHint: false is now internally consistent and more accurate for a reference server.

All 95 tests still passing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request server-everything Reference implementation for the Everything MCP server - src/everything

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants